/**
 * 
 */
package de.fsujena.chat_pi.server;

import java.io.*;
import java.net.*;
import java.util.*;
import de.fsujena.chat_pi.connection.Connector;
import de.fsujena.chat_pi.message.Message;
//import de.fsujena.chat_pi.message.*;
import de.fsujena.chat_pi.message.SysMessage;
import de.fsujena.chat_pi.message.SysMessage.*;
import de.fsujena.chat_pi.message.TextMessage;

/**
 * The message listener handles the direct communication between the server and a
 * specific client. It authenticates the user at the server and receives messages
 * from the client.
 * 
 * @author Erik Hebestreit
 * @version 1
 */
public class MessageListener extends Thread {
	int version = 1;
	//public InputStream in = null;
	//public OutputStream out = null;
	public String username;
	
	boolean isConnected;
	boolean loggedIn;
	
	Server serverObject;
	Socket client;
	
	List<TextMessage> msgArray=new ArrayList<TextMessage>();
		
	Connector connector;
	SysMessageHandler sysMsgHandler;

	/**
	 * The constructor of the message listener handles the authorization process.
	 * It expects a string input from the client containing a whitespace separated list
	 * of the username and the corresponding password. The password and the username
	 * are checked. If the authorization is successful a message put out
	 * 
	 * @param client
	 */
	public MessageListener(Socket client, Server serverObject) {
		this.client = client;
		this.serverObject = serverObject;
		
		connector = new Connector(client,true,false);
		
		isConnected=connector.init();
					
		sysMsgHandler = new SysMessageHandler(this);
		
		sendSysMessage(CATEGORY.CONNECTION,MESSAGE.CONNECTED);	
	}	
	

	public void sendUserList() {
		int i=serverObject.networkListener.messageListener.size();
		String[] userArray = new String[i];
		
		for(int n=0; n<i;n++){
			userArray[n] = serverObject.networkListener.messageListener.get(n).username;
		}
		
		sendSysMessage(CATEGORY.STATUS, MESSAGE.SEND_USER_LIST, null, userArray);
	}


	/**
	 * launch thread for receiving messages from the client
	 */
	public void run() {
		
		Object inputObject = null;
		SysMessage sysMessage;
		
		while (isConnected) {
			while (!loggedIn) {
				inputObject = null;
				
				try {
					inputObject = connector.readGeneralObject();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
					
				if (inputObject instanceof SysMessage) {
					sysMessage = (SysMessage) inputObject;
					sysMsgHandler.handleSysMessage(sysMessage);
				}
				
				if (loggedIn) {
					sendUserList();
				}
			}
			
			while (loggedIn) {
				inputObject = null;
				
				try {
					inputObject = connector.readGeneralObject();
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					close();
					// TODO: should send disconnect message
				}
					
				// TODO: the following could be implemented using polymorphism
				/*
				 * if (inputObject instanceof Message)
				 *     inputObject.handle();
				 */
				if (inputObject instanceof TextMessage){
					TextMessage tempObj = (TextMessage) inputObject;
					tempObj.setReceived(new Date());
					serverObject.postTextMessage(this.username, tempObj.getMessageText());
					msgArray.add(tempObj);
					distributeMsg(tempObj);
				} else if (inputObject instanceof SysMessage) {
					sysMessage = (SysMessage) inputObject;
					
					sysMsgHandler.handleSysMessage(sysMessage);
					
					if (sysMessage.getCategory() == CATEGORY.LOGIN)
						sendSysMessage(CATEGORY.LOGIN,MESSAGE.ALREADY_LOGGED_IN);			
				} else if (inputObject instanceof String){
					String temp = (String) inputObject;
					
					if (temp.isEmpty() == false)
						serverObject.postTextMessage(this.username,temp);
				}
			}
		}
	}
	
	public void sendSysMessage(SysMessage.CATEGORY cat, SysMessage.MESSAGE msg) {
		SysMessage sysMsg = new SysMessage(cat, msg);
		sysMsg.setSent(new Date());
		sysMsg.setSenderUsername(username);
		connector.sendObject(sysMsg);
	}
	
	public void sendSysMessage(SysMessage.CATEGORY cat, SysMessage.MESSAGE msg, String msgStr) {
		SysMessage sysMsg = new SysMessage(cat, msg, msgStr);
		sysMsg.setSent(new Date());
		sysMsg.setSenderUsername("SERVER");
		connector.sendObject(sysMsg);
	}
	
	public void sendSysMessage(SysMessage.CATEGORY cat, SysMessage.MESSAGE msg, String msgStr, Object msgObj) {
		SysMessage sysMsg = new SysMessage(cat, msg, msgStr);
		sysMsg.setMessageObject(msgObj);
		sysMsg.setSent(new Date());
		sysMsg.setSenderUsername("SERVER");
		connector.sendObject(sysMsg);
	}

	/**
	 * distributes messages to other clients
	 * 
	 * @param msg Message Object containing the message
	 */	
	public void distributeMsg(Message msg){
		int i=serverObject.networkListener.messageListener.size()-1;
		
		for(int n=0; n<=i;n++){
			if(!serverObject.networkListener.messageListener.get(n).username.equals(this.username)){
				serverObject.networkListener.messageListener.get(n).connector.sendMessageObject(msg);	
			}
		}
		
		return;
	}
	/**
	 * closes socket, disconnect user and free resources
	 * eventually has to be implemented in NetworkListener
	 */
	public void close(){
		isConnected=false;
		SysMessage temp=new SysMessage(CATEGORY.CONNECTION,MESSAGE.DISCONNECTED,username);
		temp.setSent(new Date());
		temp.setSenderUsername("SERVER");
		distributeMsg(temp);
		serverObject.networkListener.removeClient(username);
		serverObject.postInfo(String.format("user "+this.username+" disconnected"));
		/*try {
			client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}*/
		
	}
	
	/**
	 * deletes username so client can login as an other user
	 */
	public void logout(){
		username="";
		loggedIn=false;
		SysMessage temp=new SysMessage(CATEGORY.LOGIN,MESSAGE.LOGGED_OUT,username);
		temp.setSent(new Date());
		temp.setSenderUsername("SERVER");
		distributeMsg(temp);
		connector.sendMessageObject(temp);
		
	}
}
